module Manager {

    export class SocketManager extends Laya.EventDispatcher {
        public static host: string = "127.0.0.1";
        public static port: number = 15000;

        public static socketManager: SocketManager = null;

        private socket: Laya.Socket = null;
        private output: Laya.Byte;

        private PROTOCOL: string = "JSON";
        private APIConfig: Object = {};
        private PacketIds: Object = {};

        constructor() {
            super();

            if (SocketManager.socketManager instanceof SocketManager) {
                throw new Error("Socket Manager Is An Instance.");
            }
            
            // this.socket = new Laya.Socket();
			// this.socket.connect("echo.websocket.org", 80);
			// this.socket.connectByUrl("ws://echo.websocket.org:80");
            this.socket = new Laya.Socket(SocketManager.host, SocketManager.port);
            this.output = this.socket.output;

            this.socket.on(Laya.Event.OPEN, this, this.onOpen);
            this.socket.on(Laya.Event.CLOSE, this, this.onClose);
            this.socket.on(Laya.Event.ERROR, this, this.onError);
            this.socket.on(Laya.Event.MESSAGE, this, this.onMessage);

            this.APIConfig = Laya.loader.getRes("res/packet.api.json");
            for (let k in this.APIConfig) { if (this.APIConfig.hasOwnProperty(k) && typeof this.APIConfig[k] == "number") {
                this.PacketIds[this.APIConfig[k]] = k.replace(/^TO_|^ON_/, "").toLowerCase().replace(/_(\S)/g, (all, letter)=> {
                    return letter.toUpperCase()
                });
            }}

            console.log("APIConfig: ", this.APIConfig);
            console.log("PacketIds: ", this.PacketIds);
        }

        public static getInstance(): SocketManager {
            if (!SocketManager.socketManager || !(SocketManager.socketManager instanceof SocketManager)) {
                SocketManager.socketManager = new SocketManager();
            }
            return SocketManager.socketManager;
        }

        private onOpen(): void {
            console.log("Connected");

            // // Send String / ArrayBuffer
			// this.socket.send("demonstrate <sendString>");

            // // Send Bytes
			// var message: string = "demonstrate <output.writeByte>";
			// for (var i: number = 0; i < message.length; ++i) {
			// 	this.output.writeByte(message.charCodeAt(i));
			// }
			// this.socket.flush();
        }

        private onMessage(message: any): void {

            // console.log("Message from server: ");
			// if (typeof message == "String") {
			// 	console.log(message);
			// } else if (message instanceof ArrayBuffer) {
			// 	console.log(new Laya.Byte(message).readUTFBytes());
			// }

            let data = this.decode(message);
			this.socket.input.clear();

            switch (data.packetId) {
                // @Todo: Parse Data Here
            }
        }

        private onClose(): void {
            console.log("Socket Closed");
        }

        private onError(e: Laya.Event): void {
            console.log("Socket Error");
            this.socket.close();
        }

        public sendPacket(packetId: number, message: Object): void {
            if (!this.PacketIds[packetId]) return;

            if (this.socket.connected === true) {
                let msg = { "packetId": packetId, "timestamp": new Date().valueOf() };
                msg[this.PacketIds[packetId]] = message || {};

                let data = this.encode(msg);
                // console.log("Send Server Message  [ %j ]", data);
                this.socket.send(data);
            } else {
                console.error("Socket Disconnected While Sending Message.");
            }
        }

        public static parseSafe(body): Object {
            let ret;
            try {
                ret = JSON.parse(body);
            } catch (e) {
                console.error("Parse JSON Data [ %s ] Failed!", body);
                throw (e);
            }
            return ret;
        }

        /**
         * Decode Message
         * @param {String|Buffer} message
         * @return {Object}
         */
        private decode(message): any {
            let msg = {};

            switch (this.PROTOCOL) {
                case "JSON":
                    msg = SocketManager.parseSafe(message);
                    break;
                case "ProtoBufNative":
                    break;
                case "ProtoBufJS":
                    msg = ProtocolBuffer.getInstance().decodeData(message);
                    this.socket.input.clear();
                    break;
                default:
                    break;
            }
            return msg;
        }

        /**
         * Encode Message
         * @param {Object} message
         * @return {String}
         */
        private encode(message): any {
            if (!message || typeof message != "object") {
                return null;
            }
            message.timestamp = new Date().valueOf();

            let sender, msg;
            switch (this.PROTOCOL) {
                case "JSON":
                    msg = JSON.stringify(message);
                    break;
                case "ProtoBufNative":
                    break;
                case "ProtoBufJS":
                    msg = ProtocolBuffer.getInstance().encodeData(msg);
                    break;
                default:
                    break;
            }
            return msg;
        }

    }
}